/*
 * ContextFreeTreeTransformer.java
 *
 * Created on December 12, 2006, 4:34 PM
 *
 
 */
package galronnlp.util;

import galronnlp.perceptron.Node;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Iterator;
import java.util.LinkedList;
/**
 * <code>ContextFreeTreeTransformer.java<code>.
 * Takes a <code>Tree<code> object and removes indices on
 * category labels and empty element nodes (including traces and
 * empty categories).  The return value is an N-ary tree without
 * these elements.  In the event that a category dominates only
 * empty elements, it is also removed.
 *
 * (Why am I not using TreeSurgeon, you ask?  Well, I would if
 * I had the patience to re-compile it under Java 1.5. without a build file.)
 *
 * @author Dennis N. Mehay
 */
public class ContextFreeTreeTransformer {
    
    SymbolGenerator gen = null;
    
    /** Creates a new instance of ContextFreeTreeTransformer */
    public ContextFreeTreeTransformer() { this(new SymbolGenerator()); }
    
    /** Creates a new instance of ContextFreeTreeTransformer
     *  @param gen A <code>SymbolGenerator<code> passed in by a client.
     */
    public ContextFreeTreeTransformer(SymbolGenerator gen) {
        this.gen = gen;
    }
    
    /** @param t A <code>Tree<code> to transform to CF form.
     *  @return A <code>Tree<code> that is the CF version of the input tree.
     */
    public Tree contextFreeTransform(Tree t) {
        if(t.type() == Tree.LEXICAL) {
            if(t.cat().toString().contains("NONE")) {
                return null;
            } else {
                return t;
            }
        } else {
            // First, remove indices, etc. from the category label.
            Symbol c = removeIndices(t.cat());
            t.setCat(c);            
            if(t.type() == Tree.UNARY) {
                Object temp = this.contextFreeTransform((Tree)(t.getChildren().getFirst()));
                if(temp==null) {
                    // This was an empty element.  Propagate the "emptiness"
                    return null;
                } else {
                    return new UnaryTree(t.cat(),((Tree)temp),t.prob());
                }
            } else if(t.type() == Tree.BINARY) {
                Object temp1 = this.contextFreeTransform((Tree)(t.getChildren().getFirst()));
                Object temp2 = this.contextFreeTransform((Tree)(t.getChildren().getLast()));
                // Excise any null subtrees.
                if(temp1==null) {
                    if(temp2==null) {
                        return null;
                    } else {
                        return new UnaryTree(t.cat(),((Tree)temp2),t.prob());
                    }
                } else {
                    if(temp2==null) {
                        return new UnaryTree(t.cat(),((Tree)temp1),t.prob());
                    } else {
                        return new BinaryTree(t.cat(),((Tree)temp1),((Tree)temp2),t.prob());
                    }
                }
            } else {
                // It's N-ary.  Break out the big guns.
                Symbol parent = t.cat();
                LinkedList<Tree> nonNullChildren = new LinkedList<Tree>();
                                   
                Object tempObj;
                for(Iterator<Tree> currentChildren = t.getChildren().iterator(); currentChildren.hasNext(); ) {
                    tempObj = this.contextFreeTransform(currentChildren.next());
                    if(tempObj!=null) {
                        nonNullChildren.add(((Tree)tempObj));
                    } 
                }
                // Now, see how many subtrees survived the transform.
                if(nonNullChildren.size()==0) {
                    return null;
                } else if(nonNullChildren.size()==1) {
                    return new UnaryTree(t.cat(), nonNullChildren.getFirst(),t.prob());
                } else if(nonNullChildren.size()==2) {
                    return new BinaryTree(t.cat(), nonNullChildren.getFirst(), 
                                          nonNullChildren.getLast(), t.prob());
                } else {
                    return new NaryTree(t.cat(), nonNullChildren, t.prob());                    
                }
            }
        }
    }
    
    /** 
     * A method to remove indices and other context-sensitive markers from
     * category labels: e.g., NP-SUBJ-1 ==> NP.
     * @param cat A <code>Symbol<code> encapsulating the category <code>String<code>.
     * @return The <code>Symbol<code> that encapsulates the same <code>String<code> sans
     *  any indices or other function-marking nonsense.
     */
    public Symbol removeIndices(Symbol cat) {
        String catString = cat.toString();
        String[] parts = catString.split("-");
        return gen.create(parts[0]);
    }
}


